/*****************************************************************************
* 
*  This library is free software; you can redistribute it and/or
*  modify it under the terms of the GNU Lesser General Public
*  License as published by the Free Software Foundation; either
*  version 2.1 of the License, or (at your option) any later version.
*
*  This library is distributed in the hope that it will be useful,
*  but WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
*  Lesser General Public License for more details.
*
*  You should have received a copy of the GNU Lesser General Public
*  License along with this library; if not, write to the Free
*  Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
*  Boston, MA 02110-1301 USA
*
*****************************************************************************/

/*
* This code is lifted from the Linux kernel include/linux/list.h.
* It's been slightly modified to remove the dependency on prefetch.h and
* to comply with local naming conventions.
*/

#ifndef NS_LIST_H
#define NS_LIST_H

#include <stddef.h>

#ifndef barrier
#ifdef GCC
#define barrier() __asm__ __volatile__("": : :"memory")
#else
#define barrier()	(void*)0
#endif
#endif

/*
* These are non-NULL pointers that will result in page faults
* under normal circumstances, used to verify that nobody uses
* non-initialized list entries.
*/
#define NS_LIST_POISON1  ((void *) 0x00100100)
#define NS_LIST_POISON2  ((void *) 0x00200200)

/*
* Simple doubly linked list implementation.
*
* Some of the internal functions ("__xxx") are useful when
* manipulating whole lists rather than single entries, as
* sometimes we already know the next/prev entries and we can
* generate better code by using them directly rather than
* using the generic single-entry routines.
*/

struct NS_list_head {
	NS_list_head *next;
	NS_list_head *prev;
};

#define NS_LIST_HEAD_NODE(node) { &(node), &(node) }

#define NS_LIST_HEAD(node) \
NS_list_head node = NS_LIST_HEAD_NODE(node)

#define NS_LIST_HEAD_INIT(ptr) do { 			\
	(ptr)->next = (ptr); (ptr)->prev = (ptr); 	\
} while (0)

/*
* Insert a newnode entry between two known consecutive entries. 
*
* This is only for internal list manipulation where we know
* the prev/next entries already!
*/
static inline void __NS_list_add(NS_list_head *newnode,
NS_list_head *prev,
NS_list_head *next)
{
	next->prev = newnode;
	newnode->next = next;
	newnode->prev = prev;
	prev->next = newnode;
	barrier();
}

/**
* NS_list_add - add a newnode entry
* @newnode: newnode entry to be added
* @head: NS_list head to add it after
*
* Insert a newnode entry after the specified head.
* This is good for implementing stacks.
*/
static inline void NS_list_add(NS_list_head *newnode, NS_list_head *head)
{
	__NS_list_add(newnode, head, head->next);
}

/**
* NS_list_add_tail - add a newnode entry
* @newnode: newnode entry to be added
* @head: list head to add it before
*
* Insert a newnode entry before the specified head.
* This is useful for implementing queues.
*/
static inline void NS_list_add_tail(NS_list_head *newnode, NS_list_head *head)
{
	__NS_list_add(newnode, head->prev, head);
}

/*
* Delete a list entry by making the prev/next entries
* point to each other.
*
* This is only for internal list manipulation where we know
* the prev/next entries already!
*/
static inline void __NS_list_del(NS_list_head *prev, NS_list_head *next)
{
	next->prev = prev;
	prev->next = next;
	barrier();
}

/**
* NS_list_del - deletes entry from list.
* @entry: the element to delete from the list.
* Note: list_empty on entry does not return true after this, the entry is in an undefined state.
*/
static inline void NS_list_del(NS_list_head *entry)
{
	__NS_list_del(entry->prev, entry->next);
	entry->next = (NS_list_head *)NS_LIST_POISON1;
	entry->prev = (NS_list_head *)NS_LIST_POISON2;
}

/**
* NS_list_del_init - deletes entry from list and reinitialize it.
* @entry: the element to delete from the list.
*/
static inline void NS_list_del_init(NS_list_head *entry)
{
	__NS_list_del(entry->prev, entry->next);
	NS_LIST_HEAD_INIT(entry); 
}

/**
* NS_list_move - delete from one list and add as another's head
* @list: the entry to move
* @head: the head that will precede our entry
*/
static inline void NS_list_move(NS_list_head *list, NS_list_head *head)
{
	__NS_list_del(list->prev, list->next);
	NS_list_add(list, head);
}

/**
* NS_list_move_tail - delete from one list and add as another's tail
* @list: the entry to move
* @head: the head that will follow our entry
*/
static inline void NS_list_move_tail(NS_list_head *list,
NS_list_head *head)
{
	__NS_list_del(list->prev, list->next);
	NS_list_add_tail(list, head);
}

/**
* NS_list_empty - tests whether a list is empty
* @head: the list to test.
*/
static inline int NS_list_empty(NS_list_head *head)
{
	return head->next == head;
}

static inline void __NS_list_splice(NS_list_head *list,
NS_list_head *head)
{
	NS_list_head *first = list->next;
	NS_list_head *last = list->prev;
	NS_list_head *at = head->next;

	first->prev = head;
	head->next = first;

	last->next = at;
	at->prev = last;
}

/**
* NS_list_splice - join two lists
* @list: the newnode list to add.
* @head: the place to add it in the first list.
*/
static inline void NS_list_splice(NS_list_head *list, NS_list_head *head)
{
	if (!NS_list_empty(list))
		__NS_list_splice(list, head);
}

/**
* NS_list_splice_init - join two lists and reinitialize the emptied list.
* @list: the newnode list to add.
* @head: the place to add it in the first list.
*
* The list at @list is reinitialized
*/
static inline void NS_list_splice_init(NS_list_head *list,
NS_list_head *head)
{
	if (!NS_list_empty(list)) {
		__NS_list_splice(list, head);
		NS_LIST_HEAD_INIT(list);
	}
}

/**
* NS_list_entry - get the struct for this entry
* @ptr:	the &struct list_head pointer.
* @type:	the type of the struct this is embedded in.
* @member:	the name of the list_struct within the struct.
*/
#define NS_list_entry(ptr, type, member) \
	((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))

/**
* NS_list_for_each	-	iterate over a list
* @position:	the &struct list_head to use as a loop counter.
* @nextnode:	the &struct list_head to use for store next list node.
* @head:	the head for your list.
*/
#define NS_list_for_each(position, nextnode, head) \
	for (position = (head)->next, nextnode = position->next; position != (head); \
	position = nextnode, nextnode = position->next)

/*****************************************************************************
* Ordered lists - extends the regular NS_list_head features.
*****************************************************************************/

typedef int (*NS_list_compare_fn_t)(void *key1, void *key2, int key_len);

struct NS_ord_list_head {
	struct NS_ord_list_head 	*next, *prev; /* must be first */
	NS_list_compare_fn_t		compare;
	int 				key_len;
	void 				*key;
};

#define NS_ORD_LIST_HEAD_NODE(name, cmp, key, len) { &(name), &(name), cmp, len, key }

#define NS_ORD_LIST_HEAD(name, cmp, key, len) \
struct NS_ord_list_head name = NS_ORD_LIST_HEAD_NODE(name, cmp, key, len)

#define NS_ORD_LIST_HEAD_INIT(_ptr, _cmp, _key, _len) do { \
	(_ptr)->next = (_ptr); (_ptr)->prev = (_ptr); \
	(_ptr)->compare = _cmp; (_ptr)->key = _key; (_ptr)->key_len = _len; \
} while (0)

/**
* NS_ord_list_add - add a newnode entry to a keyed list
* @newnode: newnode entry to be added
* @head: list head to add it after
*
* Insert a newnode entry into the specified list head.
* This is good for implementing ordered lists.
*/
static inline void NS_ord_list_add(struct NS_ord_list_head *newnode, struct NS_ord_list_head *head)
{
	struct NS_ord_list_head *entry;
	struct NS_ord_list_head *tmp;

	for (entry = head->next, tmp = entry->next; 
		entry != head; 
		entry = tmp, tmp = entry->next) {
			if (head->compare(entry->key, newnode->key, newnode->key_len) > 0) {
				NS_list_add_tail((NS_list_head *)newnode, (NS_list_head *)entry);
				return;
			}
	}
	NS_list_add_tail((NS_list_head *)newnode, (NS_list_head *)head);
}

/**
* NS_ord_list_del - deletes entry from list.
* @entry: the element to delete from the list.
* Note: ord_list_empty on entry does not return true after this, the entry is in an undefined state.
*/
static inline void NS_ord_list_del(struct NS_ord_list_head *entry)
{
	NS_list_del((NS_list_head *) entry);
}

/**
* NS_ord_list_del_init - deletes entry from list and reinits it.
* @entry: the element to delete from the list.
* Note: ord_list_empty on entry does not return true after this, the entry is in an undefined state.
*/
static inline void NS_ord_list_del_init(struct NS_ord_list_head *entry)
{
	NS_list_del_init((NS_list_head *) entry);
}

/**
* NS_ord_list_empty - tests whether an ordered list is empty
* @head: the list to test.
*/
static inline int NS_ord_list_empty(struct NS_ord_list_head *head)
{
	return head->next == head;
}

/**
* NS_ord_list_find - find an entry with the specified key in a keyed list
* @head: the list to search
* @key: the key to find
* @len: the key length (or max length)
*
*/
static inline struct NS_ord_list_head *NS_ord_list_find(struct NS_ord_list_head *head, void *key, int key_len)
{
	struct NS_ord_list_head *entry;
	struct NS_ord_list_head *tmp;

	for (entry = head->next, tmp = entry->next; 
		entry != head; 
		entry = tmp, tmp = entry->next) {
			if (head->compare(entry->key, key, key_len) == 0) {
				return entry;
			}
	}
	return NULL;
}

/**
* NS_ord_list_find_next - find an entry with the specified key in a keyed list
* @head: the list to search
* @key: the key to find
* @len: the key length (or max length)
*
*/
static inline struct NS_ord_list_head *NS_ord_list_find_next(struct NS_ord_list_head *head, void *key, int key_len)
{
	struct NS_ord_list_head *entry;
	struct NS_ord_list_head *tmp;

	for (entry = head->next, tmp = entry->next; 
		entry != head; 
		entry = tmp, tmp = entry->next) {
			if (head->compare(entry->key, key, key_len) > 0) {
				return entry;
			}
	}
	return NULL;
}

/*****************************************************************************
* Hash lists - Double linked lists with a single pointer list head.
* Mostly useful for hash tables where the two pointer list head is
* too wasteful.
* You lose the ability to access the tail in O(1).
*****************************************************************************/

struct NS_hlist_head {
	struct NS_hlist_node *first;
};

struct NS_hlist_node {
	struct NS_hlist_node *next, **pprev;
};

#define NS_HLIST_HEAD(name) struct NS_hlist_head name = {  .first = NULL }
#define NS_HLIST_HEAD_INIT(ptr) ((ptr)->first = NULL)
#define NS_HLIST_NODE_INIT(ptr) ((ptr)->next = NULL, (ptr)->pprev = NULL)

static inline int NS_hlist_unhashed(const struct NS_hlist_node *h)
{
	return !h->pprev;
}

static inline int NS_hlist_empty(const struct NS_hlist_head *h)
{
	return !h->first;
}

static inline void __NS_hlist_del(struct NS_hlist_node *n)
{
	struct NS_hlist_node *next = n->next;
	struct NS_hlist_node **pprev = n->pprev;
	*pprev = next;
	if (next)
		next->pprev = pprev;
}

static inline void NS_hlist_del(struct NS_hlist_node *n)
{
	__NS_hlist_del(n);
	n->next = (struct NS_hlist_node *)NS_LIST_POISON1;
	n->pprev = (struct NS_hlist_node **)NS_LIST_POISON2;
}

static inline void NS_hlist_del_init(struct NS_hlist_node *n)
{
	if (n->pprev)  {
		__NS_hlist_del(n);
		NS_HLIST_NODE_INIT(n);
	}
}

static inline void NS_hlist_add_head(struct NS_hlist_node *n, struct NS_hlist_head *h)
{
	struct NS_hlist_node *first = h->first;
	n->next = first;
	if (first)
		first->pprev = &n->next;
	h->first = n;
	n->pprev = &h->first;
}

static inline void NS_hlist_add_before(struct NS_hlist_node *n, struct NS_hlist_node *next)
{
	n->pprev = next->pprev;
	n->next = next;
	next->pprev = &n->next;
	*(n->pprev) = n;
}

static inline void NS_hlist_add_after(struct NS_hlist_node *n, struct NS_hlist_node *next)
{
	next->next = n->next;
	n->next = next;
	next->pprev = &n->next;

	if(next->next)
		next->next->pprev  = &next->next;
}

#define NS_hlist_entry(ptr, type, member) \
	((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))

#define NS_hlist_for_each(pos, n, head) \
	for (pos = (head)->first; pos && ({ n = pos->next; 1; }); \
	pos = n)

#endif /* NS_LIST_H */
